package com.example.dynamicdatasource.config.aspect;

import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.example.dynamicdatasource.config.annotation.DynamicDataSource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;

@Aspect
@Component
@Slf4j
public class ServiceDataSourceAspect {

    private final SpelExpressionParser parser = new SpelExpressionParser();
    private final LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();

    /**
     * 切入点：拦截所有被@DynamicDataSource注解标记的Service方法
     */
    @Around("@annotation(dynamicDataSource) && execution(* com.example.dynamicdatasource.service..*(..))")
    public Object around(ProceedingJoinPoint joinPoint, DynamicDataSource dynamicDataSource) throws Throwable {
        try {
            // 1. 获取方法入参中指定参数的值（支持SpEL表达式，如"#paramName"）
            String paramName = dynamicDataSource.paramName();
            String paramValue = getParamValue(joinPoint, paramName);

            if (paramValue != null && !paramValue.isEmpty()) {
                // 2. 切换数据源（paramValue即为数据源标识，如"beijing"、"shanghai"）
                log.info("根据入参 {} = {} 切换数据源", paramName, paramValue);
                DynamicDataSourceContextHolder.push(paramValue);
            } else {
                log.warn("入参 {} 为空，使用默认数据源", paramName);
            }

            // 3. 执行原方法（Mapper层会自动使用切换后的数据源）
            return joinPoint.proceed();
        } finally {
            // 4. 清除数据源上下文，避免线程污染
            DynamicDataSourceContextHolder.clear();
        }
    }

    /**
     * 解析方法入参，获取指定参数的值（支持SpEL表达式）
     */
    /**
     * 解析方法入参，获取指定参数的值（支持SpEL表达式）
     */
    private String getParamValue(ProceedingJoinPoint joinPoint, String paramName) {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        String[] paramNames = discoverer.getParameterNames(method);

        // 新增日志：打印方法参数名列表（调试用）
        log.debug("方法 {} 参数名列表：{}", method.getName(), Arrays.toString(paramNames));

        if (paramNames == null || paramNames.length == 0) {
            log.error("无法获取方法 {} 的参数名，请检查编译配置（需保留调试信息）", method.getName());
            return null;
        }

        EvaluationContext context = new StandardEvaluationContext();
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < paramNames.length; i++) {
            context.setVariable(paramNames[i], args[i]);
            // 新增日志：打印参数名和对应值（调试用）
            log.debug("参数 {} = {}", paramNames[i], args[i]);
        }

        try {
            Expression expression = parser.parseExpression(paramName);
            Object value = expression.getValue(context);
            log.debug("SpEL表达式 {} 解析结果：{}", paramName, value);
            return value != null ? value.toString() : null;
        } catch (Exception e) {
            log.error("SpEL表达式 {} 解析失败", paramName, e);
            return null;
        }
    }

}
